package me.itsoo.artemis.framework.core.util;

import com.fasterxml.jackson.databind.JavaType;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.time.temporal.Temporal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

/**
 * ReflectUtils
 *
 * @author zxy
 * @version 0.0.1
 * @since 2023/5/12
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ReflectUtils {

    /**
     * 是否复杂数据类型
     *
     * @param clazz 原始类型
     * @return true 复杂数据类型，false 基本数据类型
     */
    public static boolean isConvertibleClass(Class<?> clazz) {
        return clazz != null && !clazz.isPrimitive() && !clazz.isAnnotation()
                && !Void.class.isAssignableFrom(clazz) && !CharSequence.class.isAssignableFrom(clazz)
                && !Optional.class.isAssignableFrom(clazz) && !Boolean.class.isAssignableFrom(clazz)
                && !Character.class.isAssignableFrom(clazz) && !Number.class.isAssignableFrom(clazz)
                && !Date.class.isAssignableFrom(clazz) && !Temporal.class.isAssignableFrom(clazz);
    }

    /**
     * 获取原始类型
     *
     * @param genericType Type
     * @return Class
     */
    public static Class<?> getGenericClass(Type genericType) {
        if (genericType instanceof ParameterizedType) {
            return (Class<?>) ((ParameterizedType) genericType).getRawType();
        }

        if (genericType instanceof JavaType) {
            return ((JavaType) genericType).getRawClass();
        }

        return (genericType instanceof Class) ? (Class<?>) genericType : genericType.getClass();
    }

    /**
     * 获取全部类类型
     *
     * @param clazz Class
     * @return Class[]
     */
    public static Class<?>[] getAllSuperclasses(Class<?> clazz) {
        final Class<?>[] emptyArray = new Class<?>[0];
        if (clazz == null) {
            return emptyArray;
        }

        final List<Class<?>> result = new ArrayList<>();
        Class<?> superclass = clazz.getSuperclass();
        while (superclass != null) {
            result.add(superclass);
            superclass = superclass.getSuperclass();
        }

        return result.toArray(emptyArray);
    }

    /**
     * 获取全部接口类型
     *
     * @param clazz Class
     * @return Class[]
     */
    public static Class<?>[] getAllInterfaces(Class<?> clazz) {
        final Class<?>[] emptyArray = new Class<?>[0];
        if (clazz == null) {
            return emptyArray;
        }

        final Set<Class<?>> result = new LinkedHashSet<>();
        getAllInterfaces(clazz, result);

        return result.toArray(emptyArray);
    }

    private static void getAllInterfaces(Class<?> type, Set<Class<?>> ifacesFound) {
        while (type != null) {
            final Class<?>[] interfaces = type.getInterfaces();

            for (final Class<?> i : interfaces) {
                if (ifacesFound.add(i)) {
                    getAllInterfaces(i, ifacesFound);
                }
            }

            type = type.getSuperclass();
        }
    }

    /**
     * 获取实例化对象
     *
     * @param objClass Class
     * @param <T>      T
     * @return T
     */
    public static <T> T newInstance(Class<T> objClass) {
        try {
            return objClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            return null;
        }
    }

    /**
     * 获取字段
     *
     * @param obj       Object
     * @param fieldName String
     * @return Field
     */
    @SuppressWarnings("rawtypes")
    public static Field getField(Object obj, String fieldName) {
        if (obj == null) {
            return null;
        }

        try {
            final Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
            return Objects.nonNull(clazz) ? clazz.getDeclaredField(fieldName) : null;
        } catch (NoSuchFieldException e) {
            return null;
        }
    }

    /**
     * 获取字段值
     *
     * @param field Field
     * @param obj   Object
     * @return Object
     */
    @SuppressWarnings("squid:S3011")
    public static Object getFieldValue(Field field, Object obj) {
        if (field != null && obj != null) {
            if (isConvertibleClass(obj.getClass())) {
                try {
                    field.setAccessible(true);
                    return field.get(obj);
                } catch (Exception e) {
                    return null;
                }
            }
            return obj;
        }
        return null;
    }

    /**
     * 设置字段值
     *
     * @param field Field
     * @param obj   Object
     * @param value Object
     */
    @SuppressWarnings({"squid:S3011", "squid:S108"})
    public static void setFieldValue(Field field, Object obj, Object value) {
        if (field != null) {
            try {
                field.setAccessible(true);
                field.set(obj, value);
            } catch (Exception ignore) {
            }
        }
    }

    /**
     * 获取方法名
     *
     * @param method Method
     * @return String
     */
    @SuppressWarnings("squid:S3011")
    public static String getMethodName(Method method) {
        if (method != null) {
            try {
                method.setAccessible(true);
                return method.getName();
            } catch (Exception ignore) { // NOSONAR
            }
        }

        return null;
    }

    /**
     * 获取方法名
     *
     * @param method Method
     * @return String
     */
    public static String getMethodNameNonNull(Method method) {
        final String result = getMethodName(method);
        Assert.notNull(result, "'methodName' cannot be null");
        return result;
    }

    /**
     * 获取指定类的所有方法
     *
     * @param clazz Class
     * @return Method[]
     */
    public static Method[] getAllMethods(Class<?> clazz) {
        Method[] result = clazz.getDeclaredMethods();

        Class<?>[] allSuperclasses = getAllSuperclasses(clazz);
        for (Class<?> superclass : allSuperclasses) {
            result = ArrayUtils.merge(result, superclass.getDeclaredMethods());
        }

        return result;
    }

    /**
     * 获取指定类的所有方法名
     *
     * @param clazz Class
     * @return String[]
     */
    public static String[] getAllMethodNames(Class<?> clazz) {
        final Method[] allMethods = getAllMethods(clazz);
        return Arrays.stream(allMethods).map(Method::getName).toArray(String[]::new);
    }
}
